#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <signal.h>

#define PrintersMax 2
#define ScannersMax 1
#define ModemMax 1
#define CDdriveMax 2
#define MemoryMax 1024

#define input "dispatchlist"


typedef struct node
{
	struct node *prev;
	struct node *next;

	/*Data Variables*/
	int arrival_time; 
	int priority; 
	int processor_time; 
	int Mbytes; 
	int printers; 
	int scanners; 
	int modems; 
	int CDs;
	
	int time_remaining;
	int data;
	pid_t nodePID;

}node;

typedef struct queue{
	struct node* head;
	struct node* tail;
}queue;

/*Linked List methods*/
int insertAfter(node* , node* );
int single_insertBeginning(queue* , node* );
node* removeAfter(node*);
node* removeBeginning(queue*);

/*Doubly Linked List methods*/
int insertBefore( queue* , node* , node* );
int double_insertBeginning( queue*, node* );
int double_insertAfter(node*, node*);
int insertEnd(queue*, node*);
node* removeEnd( queue* , node* );

int main (int argc, const char * argv[]) {
/*	
	if (argc != 2 ) { 
		fprintf(stderr,"Invalid number of arguments\n"); 
		return -1;
	}
*/

//File is argv[1], Could check to see if argv[1] matches inputFile name.

	FILE *jobs;
	char inputFile[50];

/*	strcpy(inputFile, argv[1]);
	if(strcmp(inputFile, input) != 0)
	{
		fprintf(stderr,"File name does not match requirements.\n"); 
		return -1;
	}
*/

	queue arrival;
	arrival.head = NULL;
	arrival.tail = NULL;
	
	node elementRead;
	elementRead.prev = NULL;
	elementRead.next = NULL;
	elementRead.data = 0;
	
	node* elementSend;
	
	node* testNode;	//testNode to index through entire Linked List
	node* removed1;

	jobs = fopen(input, "r" );	
	
	if(jobs == NULL)	//File not readable.
	{
		fprintf(stderr,"File not readable\n");
		return -1;
	}
	else
	{
	printf("\nDoubly Linked List Implementation \nReading all jobs from file ''dispatchlist'' and diplaying priorities. \nReading from comma-separated values.");
	
	fscanf(jobs,"%d %*s %d %*s %d %*s %d %*s %d %*s %d %*s %d %*s %d \n", &elementRead.arrival_time, &elementRead.priority, &elementRead.processor_time, &elementRead.Mbytes, &elementRead.printers, &elementRead.scanners, &elementRead.modems, &elementRead.CDs );

//Initialize arrival queue with the first job.
//Allocate new memory and copy the first element read from the file.
	elementSend = malloc( sizeof(node) );
	*elementSend = elementRead;
	
	double_insertBeginning(&arrival, elementSend);
	
#ifdef DEBUG	
	//Display queue contents
	testNode = arrival.head;
	printf("\n");	
		while(testNode)
		{
			printf("%d, ",testNode->arrival_time);
			testNode = testNode->next;
		}
#endif

//Continue populating arrival queue with the rest of the jobs.
	while( fscanf(jobs,"%d %*s %d %*s %d %*s %d %*s %d %*s %d %*s %d %*s %d \n", &elementRead.arrival_time, &elementRead.priority, &elementRead.processor_time, &elementRead.Mbytes, &elementRead.printers, &elementRead.scanners, &elementRead.modems, &elementRead.CDs ) != EOF )
	{

	//Allocate new memory and copy the current element read from the file.
	elementSend = malloc( sizeof(node) );
	*elementSend = elementRead;

	testNode = arrival.head;
	while( testNode != NULL && elementSend->arrival_time < testNode->arrival_time  )
	{
		testNode = testNode->next;
	}


	if(testNode == arrival.head)	//Insertion attempt at head of queue
		double_insertBeginning(&arrival, elementSend);
	else if(testNode == NULL)	//Insertion attempt at tail of queue
		insertEnd(&arrival, elementSend);
	else				//Insertion attempt in the middle of queue
		insertBefore(&arrival, testNode, elementSend );
		
#ifdef DEBUG		
	//Display queue contents
	printf("\nTest");	
	testNode = arrival.head;
	printf("\n");	
		while(testNode)
		{
			printf("%d, ",testNode->arrival_time);
			testNode = testNode->next;
		}
#endif
	}
		
	fclose(jobs);
	}

	//Display queue contents
	testNode = arrival.head;
	printf("\n\nContents of Arrival queue:\n");	
		while(testNode)
		{
			printf("%d, ",testNode->arrival_time);
			testNode = testNode->next;
		}

//Every removeEnd() will need a free() at a later time.

//Test RemoveEnd (First Try):
/*	removed1 = removeEnd(&arrival, arrival.tail);
	testNode = arrival.head;
	
	printf("\nRemoveEnd #1: %d",removed1->arrival_time);
	printf("\n");	
	while(testNode)
	{
		printf("%d, ",testNode->arrival_time);
		testNode = testNode->next;
	}
	free(removed1);
*/
	
//Test RemoveEnd (Second Try):
/*	removed1 = removeEnd(&arrival, arrival.tail);
	testNode = arrival.head;
	
	printf("\nRemoveEnd #2: %d",removed1->arrival_time);
	printf("\n");	
	while(testNode)
	{
		printf("%d, ",testNode->arrival_time);
		testNode = testNode->next;
	}
	free(removed1);
*/

//Test RemoveEnd (Third Try):
/*	removed1 = removeEnd(&arrival, arrival.tail);
	testNode = arrival.head;
	
	printf("\nRemoveEnd #3: %d",removed1->arrival_time);
	printf("\n");	
	while(testNode)
	{
		printf("%d, ",testNode->arrival_time);
		testNode = testNode->next;
	}
	free(removed1);
*/
	
//Attempt to remove elements from an Empty Linked List
//Error.

//Test RemoveEnd (Fourth Try):
/*	removed1 = removeEnd(&arrival, arrival.tail);
	testNode = fcfs.head;


	printf("\nRemoveEnd #4: %d",removed1->arrival_time);
	printf("\n");	
	while(testNode)
	{
		printf("%d, ",testNode->arrival_time);
		testNode = testNode->next;
	}
	free(removed1);
*/
	queue fcfs;
	queue priority1;
	queue priority2;
	queue priority3;
	
	fcfs.head = NULL;
	fcfs.tail = NULL;

	priority1.head = NULL;
	priority1.tail = NULL;

	priority2.head = NULL;
	priority2.tail = NULL;

	priority3.head = NULL;
	priority3.tail = NULL;

	int current_arrival;

//Assigning nodes to appropriate queues. fcfs (Priority 0), Priority 1, Priority 2, Priority 3. 
	while( arrival.tail != NULL )
	{
		current_arrival = arrival.tail->arrival_time;	//current arrival should be time after previous CPU burst.
		
		while(arrival.tail != NULL && current_arrival <= arrival.tail->arrival_time)
		{
			removed1 = removeEnd(&arrival, arrival.tail);
			if( removed1->priority == 0)
				double_insertBeginning(&fcfs, removed1);	
			else if( removed1->priority == 1)
				double_insertBeginning(&priority1, removed1);	
			else if( removed1->priority == 2)
				double_insertBeginning(&priority2, removed1);	
			else//if( removed1->priority == 3)
				double_insertBeginning(&priority3, removed1);	
		}
	
	//Time share processor.
	//After 1 time quantum of processsor, reevaluate arrival queue's


#ifdef DEBUG	
	
	printf("\n\nArrival time = %d", current_arrival);

	//Display Arrival queue contents
	testNode = arrival.head;
	printf("\n\nContents of Arrival queue:\n");	
		while(testNode)
		{
			printf("%d, ",testNode->arrival_time);
			testNode = testNode->next;
		}

	//Display FCFS queue contents
	testNode = fcfs.head;
	printf("\n\nContents of fcfs queue :\n");	
		while(testNode)
		{
			printf("%d, ",testNode->arrival_time);
			testNode = testNode->next;
		}
	//Display Priority1 queue contents
	testNode = priority1.head;
	printf("\n\nContents of Priority 1 queue:\n");	
		while(testNode)
		{
			printf("%d, ",testNode->arrival_time);
			testNode = testNode->next;
		}
	
	//Display Priority2 queue contents
	testNode = priority2.head;
	printf("\n\nContents of Priority 2 queue:\n");	
		while(testNode)
		{
			printf("%d, ",testNode->arrival_time);
			testNode = testNode->next;
		}
	
	//Display Priority3 queue contents
	testNode = priority3.head;
	printf("\n\nContents of Priority 3 queue:\n");	
		while(testNode)
		{
			printf("%d, ",testNode->arrival_time);
			testNode = testNode->next;
		}
#endif	
	}	
	
	//Display Arrival queue contents
	testNode = arrival.head;
	printf("\n\nContents of Arrival queue:\n");	
		while(testNode)
		{
			printf("%d, ",testNode->arrival_time);
			testNode = testNode->next;
		}

	//Display FCFS queue contents
	testNode = fcfs.head;
	printf("\n\nContents of fcfs queue :\n");	
		while(testNode)
		{
			printf("%d, ",testNode->arrival_time);
			testNode = testNode->next;
		}
	//Display Priority1 queue contents
	testNode = priority1.head;
	printf("\n\nContents of Priority 1 queue:\n");	
		while(testNode)
		{
			printf("%d, ",testNode->arrival_time);
			testNode = testNode->next;
		}
	
	//Display Priority2 queue contents
	testNode = priority2.head;
	printf("\n\nContents of Priority 2 queue:\n");	
		while(testNode)
		{
			printf("%d, ",testNode->arrival_time);
			testNode = testNode->next;
		}
	
	//Display Priority3 queue contents
	testNode = priority3.head;
	printf("\n\nContents of Priority 3 queue:\n");	
		while(testNode)
		{
			printf("%d, ",testNode->arrival_time);
			testNode = testNode->next;
		}

	printf("\n\n");
	return 0;
}

/*	Linked List methods.	
 *      Adapted from Single Linked List implementation from:
 *      http://en.wikipedia.org/wiki/Linked_list 
 */ 
/* Helper methods */
/*Inserts newNode after currentNode*/
int insertAfter(node* currentNode, node* newNode) // insert newNode after node
{
    	newNode->next = currentNode->next;
     	currentNode->next = newNode;	
	return 0;
}
/*Inserts newNode at the head of the queue*/
int single_insertBeginning(queue* queue, node* newNode) // insert node before current first node
{
	if(queue->tail == NULL)
		queue->tail = newNode;

	newNode->next = queue->head;
 	queue->head = newNode;
	return 0;
}
/*Removes Node after currentNode, currentNode cannot be the tail Node*/
node* removeAfter(node* currentNode) // remove node past this one
{
	node* removedNode;
	removedNode = currentNode->next;
     	currentNode->next = currentNode->next->next;

	return removedNode;
}
/*Removes Node at head of queue*/
node* removeBeginning(queue* queue) // remove first node
{
	node* removedNode;
	removedNode = queue->head;
	queue->head = queue->head->next; // point past deleted node

	return removedNode;
}


/*	Doubly Linked List methods.	
 *      Adapted from Doubly Linked List implementation from:
 *      http://en.wikipedia.org/wiki/Doubly_linked_list 
 */                     
/* Helper methods */

/*Add newElement to before specified element of queue*/
int insertBefore( queue* queue, node* element, node* newElement){
	
	newElement->prev = element->prev;
	newElement->next = element;
	
	if ( element->prev == NULL )
		queue->head = newElement;
	else
		element->prev->next = newElement;
	
	element->prev = newElement;
	
	return 0;
}

/*Add newElement to beginning of queue*/
/*      push(node) to beginning of queue */
int double_insertBeginning( queue* queue, node* newElement){
	if (queue->head == NULL){
		queue->head = newElement;
		queue->tail = newElement;
		newElement->prev = NULL;
		newElement->next = NULL;
	}
	
	else{
		insertBefore(queue, (queue->head), newElement);
	}
	
	return 0;
}

int double_insertAfter(node* element, node* newElement){
 	newElement->next = element->next;
    	newElement->prev = element;
	
	if(element->next != NULL)	//Attempt to modify the tail->next, which is NULL.
    		element->next->prev = newElement;
	//else //InsertAfter in the middle of the queue.

    	element->next = newElement;
}

int insertEnd(queue* queue, node* newElement){
   	if (queue->tail == NULL)
  	 {
        	 newElement->prev = newElement;
        	 newElement->next = newElement;
	 }
	else
 		double_insertAfter(queue->tail, newElement);
     queue->tail = newElement;
}

/*Removes element from the End(tail) of the queue*/ 
/* pop() from end of queue when queue.tail is removed => remove(queue, queue.tail) */
node* removeEnd( queue* queue, node* element){

	node* removedNode;
	removedNode = element;	

	//Node that is returned when there is an empty list.	
	node* emptyNode;
	
	node empty;
	empty.prev = NULL;
	empty.next = NULL;
	empty.arrival_time = 0; 
	empty.priority = 0; 
	empty.processor_time = 0; 
	empty.Mbytes = 0; 
	empty.printers = 0; 
	empty.scanners = 0; 
	empty.modems = 0; 
	empty.CDs = 0;
	empty.time_remaining = 0;
	empty.data = 0;

	if(queue->tail == NULL)
	{
		emptyNode = malloc( sizeof(node) );
		*emptyNode = empty;
		return emptyNode;	
	}

	if (removedNode->prev == NULL)
		queue->head = removedNode->next;
	else
		removedNode->prev->next = removedNode->next;
	
	if (removedNode->next == NULL)
		queue->tail = removedNode->prev;
	else
		removedNode->next->prev = removedNode->prev;
	
	return removedNode;
}
